Operations, Troubleshooting & Glossary
The platform is live — this page covers the day-two work: routine maintenance, a cross-layer troubleshooting table, and a glossary of the terms used throughout the module.
Part 4 of 4 — Overview · Lab 1: Provision & Configure · Lab 2: Deploy & Release
- Rotate the warehouse database password via GitHub secrets, without SSH or manual .env edits
- Confirm Let's Encrypt TLS renewal is automated via certbot's cron
- Take dated VM snapshots before risky changes
- Diagnose cross-layer issues (host, VM, Ansible, CI/CD, app) using the troubleshooting table
- Look up unfamiliar terms in the glossary
Phase 6 — Operations & Maintenance
Rotating the database password
A plaintext DB password was previously committed and remains in git history. Rotate it on the DB server itself, then update the secret and redeploy:
ALTER USER <DB_USER> WITH PASSWORD 'new-strong-password-here';gh secret set DB_PASSWORD --body 'new-strong-password-here'A push to main (or re-running the workflow) regenerates the VM’s .env and restarts the
containers with the new value. General rule: rotating any credential = gh secret set NAME --body '…' + redeploy. No SSH or manual .env edit required.
Renewing TLS certificates
If you used Ansible playbook 04, certbot installed a daily renewal cron (certbot renew --quiet && systemctl reload nginx). Let’s Encrypt certs last 90 days; the cron keeps them fresh
automatically.
Snapshots around risky changes
Take a fresh VM snapshot before any significant change (OS update, big deploy, config edit):
sudo virsh snapshot-create-as --domain <VM_NAME> \
--name "pre-<change>-$(date +%Y%m%d)" --description "before <change>" --atomicName snapshots with a date + reason so their purpose is clear at revert time.
Phase 7 — Troubleshooting (cross-layer)
| Symptom | Layer | Fix |
|---|---|---|
| Port-forwarding rules gone after reboot | Host | sudo netfilter-persistent save (rules are volatile) |
| VM got a different IP after reboot | VM | Netplan not saved / netplan apply not run — redo Step 1.3 |
nc -zv <PUBLIC_IP> hangs from inside the VM | VM | Expected — hairpin NAT unsupported; test from an external machine |
| Port 443 refused despite nginx running | VM | Check ss -tlnp | grep 443; if empty, the sites-enabled symlink is missing — re-create it |
nginx -t syntax error | VM | Usually a missing ; or brace — sudo nginx -t 2>&1 shows the line |
| SSH asks to accept a new host key | any | VM was rebuilt — ssh-keygen -R <IP_ADDRESS> on your client |
ansible all -m ping fails | Ansible | Check inventory ansible_user/key path and that port-forward/SSH is reachable |
| GHCR pull 401/403 on first deploy | CI/CD | Give the repo Write access under each package’s Manage Actions access (Step 3.4) |
| Superset lost all dashboards / using SQLite | App | The ./.superset:/app/pythonpath volume mount is missing — restore it |
| Dagster schedule never fires | App | Confirm the schedule’s default_status=RUNNING; Dagster schedules default to stopped |
Glossary
The full glossary of terms now lives on its own page: Glossary.