n4nAI

Automating API key rotation with a secrets manager

Learn automating API key rotation secrets manager workflows with concrete code: Vault, AWS Secrets Manager, and Kubernetes for zero-downtime key cycling.

n4n Team3 min read720 words

Audio narration

Coming soon — every post will get a voice note here.

Manual key rotation through a ticketing system fails under scale. Automating API key rotation secrets manager integration gives you reproducible cycles, bounded blast radius, and an audit log that satisfies compliance without heroics. Below is an end-to-end pipeline using HashiCorp Vault and AWS, with patterns that port to GCP Secret Manager or Kubernetes.

Why programmatic rotation beats runbooks

A shell script someone runs locally is not rotation—it’s a liability with extra steps. The moment a key leaks, you need to know exactly which systems held it, what the new value is, and whether the old one is actually dead. A secrets manager makes that stateful and observable. The automation wraps the manager with provider APIs so the credential itself is never touched by human hands.

Step 1: Provision a secrets manager backend

Start with a real backend, not a JSON file in S3. For self-hosted, Vault is the default. Bring up a dev server to validate the pattern, then move to a sealed cluster with TLS.

vault server -dev -dev-root-token-id=root &
export VAULT_ADDR='http://127.0.0.1:8200'
vault login root
vault secrets enable -path=llm-keys kv-v2

If you prefer managed, AWS Secrets Manager needs no server:

aws secretsmanager create-secret --name prod/llm/gateway \
  --description "Rotated LLM provider key" --region us-east-1

The path or ARN is your stable reference. Code reads from that, never from env vars baked into images.

Step 2: Define the credential schema and store the initial value

Treat the secret as a versioned document, not a bare string. Store metadata alongside the key so rotation logic can make decisions.

{
  "api_key": "sk-old-1234567890",
  "provider": "openai",
  "created_at": "2024-05-01T12:00:00Z",
  "rotates_at": "2024-05-15T12:00:00Z",
  "status": "active"
}

Write it with the Vault SDK so you get a version counter:

import hvac

client = hvac.Client(url="http://127.0.0.1:8200", token="root")
client.secrets.kv.v2.create_or_update_secret(
    path="gateway",
    secret={
        "api_key": "sk-old-1234567890",
        "provider": "openai",
        "created_at": "2024-05-01T12:00:00Z",
        "rotates_at": "2024-05-15T12:00:00Z",
        "status": "active",
    },
    mount_point="llm-keys",
)

Step 3: Implement the rotation function

The core job: call the provider to issue a new key, confirm it works, then write it as a new version. Using AWS IAM as a stand-in for any provider with a create-key API:

import boto3
import datetime
import hvac

def rotate_iam_key(user_name: str, vault_path: str):
    iam = boto3.client("iam")
    # create new access key
    resp = iam.create_access_key(UserName=user_name)
    new_key = resp["AccessKey"]["AccessKeyId"]
    new_secret = resp["AccessKey"]["SecretAccessKey"]
    # minimal live test
    sts = boto3.client(
        "sts",
        aws_access_key_id=new_key,
        aws_secret_access_key=new_secret,
    )
    sts.get_caller_identity()

    client = hvac.Client(url="http://127.0.0.1:8200", token="root")
    client.secrets.kv.v2.create_or_update_secret(
        path=vault_path,
        secret={
            "api_key": new_key,
            "api_secret": new_secret,
            "provider": "aws-iam",
            "created_at": datetime.datetime.utcnow().isoformat() + "Z",
            "rotates_at": (datetime.datetime.utcnow() + datetime.timedelta(days=30)).isoformat() + "Z",
            "status": "active",
        },
        mount_point="llm-keys",
    )
    # disable old key after grace period in a separate step
    return new_key

For LLM providers with a REST endpoint, replace the boto3 call with requests.post to their key-management route. The pattern holds: issue, verify, write new version, schedule old-key revocation.

Step 4: Wire the automated trigger

Don’t rely on a cron on someone’s laptop. Use a managed scheduler. On AWS, EventBridge + Lambda is clean:

aws events put-rule --schedule-expression "rate(30 days)" --name llm-key-rotation
aws lambda create-function --function-name rotate-llm-key \
  --runtime python3.12 --handler rotate.lambda_handler \
  --role arn:aws:iam::123456789012:role/rotator --zip-file fileb://deploy.zip
aws events put-targets --rule llm-key-rotation --targets "Id=1,Arn=arn:aws:lambda:us-east-1:123456789012:function:rotate-llm-key"

Locally or in Vault, a systemd timer or Kubernetes CronJob works equally well. The job must be idempotent: if it runs twice, it should not create two live keys without revoking the prior.

Step 5: Propagate the new key to running services

A rotated secret is useless if your app cached the old one. Two patterns: push or pull. Pull is simpler with Vault Agent or External Secrets Operator.

Vault Agent config injects the latest version into a file:

template {
  destination = "/etc/secrets/llm_key"
  content     = "{{ with secret \"llm-keys/data/gateway\" }}{{ .Data.data.api_key }}{{ end }}"
}

For Kubernetes, External Secrets maps the Vault path to a Secret the deployment mounts:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: llm-gateway-key
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: llm-gateway-key
  data:
    - secretKey: api_key
      remoteRef:
        key: llm-keys/data/gateway
        property: api_key

Your service must re-read the file or env on SIGHUP. A small TS snippet for an Express app:

import fs from 'fs';
let apiKey = fs.readFileSync('/etc/secrets/llm_key', 'utf8');
process.on('SIGHUP', () => {
  apiKey = fs.readFileSync('/etc/secrets/llm_key', 'utf8');
});

Step 6: Schedule old-key revocation with a grace window

Immediate revocation risks dropping in-flight requests. Write the old key to a deprecated status, keep it for 24h, then delete.

def revoke_old_key(iam_user: str, old_key_id: str):
    iam = boto3.client("iam")
    iam.update_access_key(
        UserName=iam_user, AccessKeyId=old_key_id, Status="Inactive"
    )
    # after grace period
    iam.delete_access_key(UserName=iam_user, AccessKeyId=old_key_id)

Automate the second call with a delayed queue message or a separate CronJob.

Step 7: Verify success

Verification is not “the script exited 0”. Confirm the new key authenticates and the old one fails.

import requests

def verify(vault_path: str, endpoint: str):
    client = hvac.Client(url="http://127.0.0.1:8200", token="root")
    secret = client.secrets.kv.v2.read_secret_version(path=vault_path, mount_point="llm-keys")
    key = secret["data"]["data"]["api_key"]
    r = requests.get(endpoint, headers={"Authorization": f"Bearer {key}"})
    assert r.status_code == 200, "new key rejected"
    # old key should be inactive
    print("rotation verified, version", secret["data"]["metadata"]["version"])

Run this as a post-rotation check in CI or as a Lambda step. If it fails, roll back by marking the previous version active.

Cutting the rotation surface with a gateway

If you front multiple model providers through a single OpenAI-compatible endpoint, you rotate one gateway credential instead of ten per-vendor keys. n4n.ai addresses 240+ models behind one endpoint and honors client routing directives, so the secrets-manager job only needs to cycle that single token while provider-specific keys stay internal to the gateway. That shrinks the automation scope and the audit matrix.

Common pitfalls

Race conditions happen when two rotators run concurrently. Use a distributed lock—Vault’s sys/leases or DynamoDB conditional writes—before issuing new keys.

Downtime comes from apps that read env at boot and never reload. Fix the app, don’t weaken rotation frequency.

Missing rollback turns a leak into an outage. Always keep the prior version retrievable for at least one cycle.

Final checklist

  • Secrets manager backed by policy and audit log.
  • Rotation function issues, verifies, writes new version.
  • Scheduler triggers idempotently.
  • Workloads pull updated secret with reload signal.
  • Old key revoked after grace window.
  • Verification step asserts live auth.

Automating API key rotation secrets manager pipelines is infrastructure, not scripting. Build it like you would any other critical service.

Tagsapi-keyskey-rotationsecrets-managerautomation

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All api key rotation & secrets management posts →