Rotate the gordon_bot DB role password
When to use: first deploy of a fresh cluster, scheduled 90-day rotation, or on suspected credential compromise.
The gordon_bot role is created by migration 0027_gordon_bot_role.sql with a placeholder password (placeholder_rotate_via_ansible). That placeholder exists only so the role can be created at migration time. No gordon-bot container should ever run with the placeholder in place.
When to rotate
- First deploy of a fresh cluster — replace the placeholder before any bot starts.
- Scheduled rotation — every 90 days, aligned with the Binance key rotation schedule.
- On suspected compromise — immediately, followed by a full audit of
trading.bot_eventsandtrading.order_intentsfor rows the old credential may have emitted.
Prerequisites
- Access to the Ansible vault on the ops workstation.
- Superuser (or
gordon) Postgres credentials for srv-apps. - A paused deploy window — gordon-manager should not be spawning new bots during rotation.
Procedure
Generate a new password.
bashopenssl rand -hex 32Do not reuse passwords across environments.
Update the Ansible vault. The key is
trading.gordon_bot_db_passwordunderhomelab/group_vars/srv-apps/vault.yml:bashansible-vault edit homelab/group_vars/srv-apps/vault.ymlUpdate
trading.gordon_bot_db_passwordto the new value.Rotate in Postgres. Connect as
gordonorpostgreson srv-apps and run:sqlALTER ROLE gordon_bot WITH PASSWORD '<new-password>';This is cluster-scoped — one
ALTER ROLEapplies to all bots sharing the role.Via Docker:
bashssh srv-apps docker compose exec postgres psql -U gordon -d gordon \ -c "ALTER ROLE gordon_bot WITH PASSWORD '<new-password>';"Deploy the updated vault. Run the bot deploy playbook:
bashansible-playbook -i homelab/inventory/hosts.yml \ homelab/playbooks/deploy-gordon-bot.yml --ask-vault-passThe playbook renders each bot's env file with the new password and restarts the bot containers via gordon-manager's reconciler.
Verify. After each bot restarts:
GET /healthzreturns 200.GET /readyzreturns 200 after warmup and lease acquire.trading.bot_eventscarries arole_probe_passedevent with the expectedbot_idandrole: "gordon_bot".GET /configshowsdatabase_url: "[REDACTED]"— no credential leakage.
Check via curl from srv-apps:
bashcurl -fsS http://localhost:8084/healthz curl -fsS http://localhost:8084/readyz
Verify
ssh srv-apps
docker compose exec postgres psql -U gordon -d gordon -c "
SET search_path = trading;
SELECT bot_id, event_type, created_at
FROM bot_events
WHERE event_type = 'role_probe_passed'
ORDER BY created_at DESC LIMIT 5;
"Expected: one role_probe_passed row per bot, timestamped after the rotation.
Rollback
If rotation breaks bot startup:
Re-set the old password:
sqlALTER ROLE gordon_bot WITH PASSWORD '<old-password>';Re-run the deploy playbook with the vault rolled back to the previous revision:
bashgit revert <vault-commit> ansible-playbook -i homelab/inventory/hosts.yml \ homelab/playbooks/deploy-gordon-bot.yml --ask-vault-passFile a postmortem — a rotation that breaks startup usually points at an env-template drift or a missed bot in the inventory.
Related
- Migration
0027_gordon_bot_role.sql— role creation and RLS policies - Migration
0028_gordon_bot_lease_holder_grant.sql— column-level UPDATE grant onbot_leases - Bot warmup fallback — role probe is a warmup gate
- Incident response — operator token rotation