fix(zec2_rotatekeys): recreate container on -Restart so the new .env loads

A plain 'docker compose restart' reuses the container's existing environment
and would NOT pick up env_file changes, leaving the app on the old secrets
after a rotation. -Restart now runs 'up -d --force-recreate <svc>', the
reliable way to apply the new .env. Docs updated to match.
This commit is contained in:
KellyMichels 2026-07-24 13:14:55 -05:00
parent 4452f54404
commit 636e15114f
3 changed files with 23 additions and 15 deletions

View File

@ -30,8 +30,10 @@ Notable changes to the Evomedia.net Token Savers.
masked prompt and streams it over SSH stdin — never a command argument,
never echoed. Backs the server `.env` up to a timestamped `.bak` first,
updates the key atomically (matches or appends), auto-detects
`backend/.env` from `deploy.preserve`, restarts only with `-Restart`, and
`-WhatIf` previews the plan without touching anything.
`backend/.env` from `deploy.preserve`, and with `-Restart` **recreates**
the container (`up -d --force-recreate`, so the new values actually load —
a plain restart keeps the old environment). `-WhatIf` previews the plan
without touching anything.
- **`zdeploy` server-side health verification (`verify` block)** — projects
not published through the edge proxy can declare
`"verify": { "port": ..., "path": "/health", "expect": "..." }` and the

View File

@ -228,7 +228,7 @@ zstop <project> [<project> ...]
zec2_rotatekeys <project> [-Rotate KEY,KEY] [-Set KEY,KEY] [-EnvFile rel/path] [-Restart] [-WhatIf]
```
For when a secret leaks or a deploy overwrites a production `.env` with dev values: rotate or reset keys in a project's **server-side** `.env` without the values ever passing through this machine's shell history, a command argument, or your screen. `-Rotate` keys are regenerated **on the server** with `openssl rand -hex 32` — the new value is written straight into the `.env` there and never leaves the box. `-Set` keys are typed into a masked prompt and streamed to the server over SSH stdin (never a command argument, never echoed), for operator-known values like `DATABASE_URL` or `ADMIN_EMAIL`. The current server `.env` is copied to a timestamped `.bak` before any change; the KEY line is updated atomically, matching an existing key or appending it. The env file is auto-detected from the project's `deploy.preserve` (first `*.env`) or defaults to `.env` — override with `-EnvFile backend/.env`. Nothing restarts unless you pass `-Restart`. Being high-impact, it confirms before writing; `-WhatIf` prints the exact plan and changes nothing.
For when a secret leaks or a deploy overwrites a production `.env` with dev values: rotate or reset keys in a project's **server-side** `.env` without the values ever passing through this machine's shell history, a command argument, or your screen. `-Rotate` keys are regenerated **on the server** with `openssl rand -hex 32` — the new value is written straight into the `.env` there and never leaves the box. `-Set` keys are typed into a masked prompt and streamed to the server over SSH stdin (never a command argument, never echoed), for operator-known values like `DATABASE_URL` or `ADMIN_EMAIL`. The current server `.env` is copied to a timestamped `.bak` before any change; the KEY line is updated atomically, matching an existing key or appending it. The env file is auto-detected from the project's `deploy.preserve` (first `*.env`) or defaults to `.env` — override with `-EnvFile backend/.env`. Nothing touches the running app unless you pass `-Restart`, which **recreates** the container (`docker compose up -d --force-recreate <svc>`) so it actually reloads the new `.env` — a plain `restart` would keep the old environment. Being high-impact, it confirms before writing; `-WhatIf` prints the exact plan and changes nothing.
```powershell
# Preview only — see exactly what would change, change nothing:

View File

@ -20,8 +20,10 @@
# * The current server .env is copied to a timestamped .bak before any change.
# * -WhatIf prints the exact plan and touches nothing. High-impact, so it
# confirms before writing unless you pass -Confirm:$false.
# * It does NOT restart the app unless you pass -Restart (prod restarts are a
# deliberate, separate decision).
# * It does NOT touch the running app unless you pass -Restart, which
# recreates the container (up -d --force-recreate) so it reloads the new
# .env - a plain `restart` reuses the old environment. Prod restarts are a
# deliberate, separate decision.
#
# Usage:
# zec2_rotatekeys <project> [-Rotate K1,K2] [-Set K1,K2] [-EnvFile rel/path]
@ -167,13 +169,13 @@ try {
Write-Host " Env file: $remoteEnv" -ForegroundColor DarkGray
if ($Rotate.Count) { Write-Host " Rotate (fresh random, server-side): $($Rotate -join ', ')" -ForegroundColor Gray }
if ($Set.Count) { Write-Host " Set (masked prompt, streamed): $($Set -join ', ')" -ForegroundColor Gray }
if ($Restart) { Write-Host " Then: restart the app container" -ForegroundColor Gray }
if ($Restart) { Write-Host " Then: recreate the app container (reloads the new .env)" -ForegroundColor Gray }
Write-Host ""
$action = @()
if ($Rotate.Count) { $action += "rotate [$($Rotate -join ',')]" }
if ($Set.Count) { $action += "set [$($Set -join ',')]" }
if ($Restart) { $action += "restart" }
if ($Restart) { $action += "recreate" }
if (-not $PSCmdlet.ShouldProcess("${target}:$remoteEnv", ($action -join ' + '))) {
Write-Host "Preview only - no changes made." -ForegroundColor Yellow
Stop-ZTracking; exit 0
@ -244,18 +246,22 @@ try {
ssh @sshOpts $target "rm -f $remoteHelper" | Out-Null
}
# --- optional app restart -------------------------------------------------
# --- optional app recreate ------------------------------------------------
# A plain `docker compose restart` reuses the container's existing
# environment, so it would NOT pick up the .env we just edited. `up -d
# --force-recreate` rebuilds the container from current config, reloading
# env_file / environment - the reliable way to apply the new secrets.
$restarted = $false
if ($Restart -and $done.Count -gt 0) {
$composeDir = if ($proj.remote.composeDir) { $proj.remote.composeDir } else { $remotePath }
$svc = if ($proj.remote.appService) { $proj.remote.appService } else { "app" }
Write-Host ""
Write-Host " Restarting service '$svc' in $composeDir ..." -ForegroundColor Cyan
ssh @sshOpts $target "cd $composeDir && sudo COMPOSE_BAKE=false docker compose restart $svc"
if ($LASTEXITCODE -eq 0) { Write-Host " Restarted $svc." -ForegroundColor Green; $restarted = $true }
else { Write-Host " WARNING: restart of '$svc' failed (exit $LASTEXITCODE) - restart it manually." -ForegroundColor Red }
Write-Host " Recreating service '$svc' in $composeDir (to load the new .env) ..." -ForegroundColor Cyan
ssh @sshOpts $target "cd $composeDir && sudo COMPOSE_BAKE=false docker compose up -d --force-recreate $svc"
if ($LASTEXITCODE -eq 0) { Write-Host " Recreated $svc." -ForegroundColor Green; $restarted = $true }
else { Write-Host " WARNING: recreate of '$svc' failed (exit $LASTEXITCODE) - apply it manually: docker compose up -d --force-recreate $svc" -ForegroundColor Red }
} elseif ($Restart) {
Write-Host " Skipping restart - no keys were changed." -ForegroundColor Yellow
Write-Host " Skipping recreate - no keys were changed." -ForegroundColor Yellow
}
# --- summary --------------------------------------------------------------
@ -265,9 +271,9 @@ try {
if ($failed.Count) { Write-Host " Failed: $($failed -join ', ')" -ForegroundColor Red }
Write-Host " Backup: $backup (delete once verified)" -ForegroundColor DarkGray
if ($Restart -and -not $restarted -and $done.Count -gt 0) {
Write-Host " Restart: NOT done - restart the app so it loads the new values." -ForegroundColor Yellow
Write-Host " Recreate: NOT done - apply the new values with: docker compose up -d --force-recreate <svc>" -ForegroundColor Yellow
} elseif (-not $Restart -and $done.Count -gt 0) {
Write-Host " Note: the app is still running with the OLD values - restart it (or re-run with -Restart)." -ForegroundColor Yellow
Write-Host " Note: the app is still running with the OLD values - re-run with -Restart, or 'docker compose up -d --force-recreate <svc>' on the server." -ForegroundColor Yellow
}
Write-Host ""