# Deploy Workflow Local code → finished image shipped over SSH → container swapped on the host. No container registry (see DECISIONS.md D8 for why). ## The two channels to the host Both go to the same GCP box; they're used for different steps: - **`podman save … | ssh … podman load`** — ships the image *bytes*. This has to use raw `ssh` because it's a pipe into the remote Podman CLI. - **`podman --connection gcp-lab …`** — swaps the running container over the existing rootless socket, hands-free (no manual SSH). The alias is already configured and set as default: `ssh://matt@35.203.147.44:22/run/user/1001/podman/podman.sock`. > The socket is under **UID 1001** on this host (the original architecture doc's 1000 is > wrong). Using the alias means you never have to think about the UID. ## Why build locally The `e2-micro` can't build the Vue multi-stage image reliably (memory). Always build on the local workstation and ship the finished image. Never build on the host. ## `deploy.sh` Lives at the repo root and is run from there (so the relative `./backend` and `./frontend` build contexts resolve). Config block at the top holds host/user/key/alias. Usage: ```bash ./deploy.sh # full cycle: build both, ship both, swap both containers VERSION=v2 ./deploy.sh # tag images v2 instead of a timestamp → named rollback point ./deploy.sh --build --ship --swap # explicit equivalent of the default ./deploy.sh --restart-caddy # after editing the Caddyfile to add/modify routes ``` With no arguments it runs `build → ship → swap`. `VERSION` defaults to a timestamp; set it explicitly to create a named point you can redeploy later. ## Steps the script performs 1. **build** — `podman build` the backend and frontend images locally. 2. **ship** — `podman save | ssh -i matt@35.203.147.44 'podman load'` for each image. 3. **swap** — over `--connection gcp-lab`: `rm -f` then `run -d` each container with its name and port mapping. 4. **restart-caddy** (on demand) — `podman --connection gcp-lab restart caddy-proxy`, needed only when the Caddyfile changed (e.g. new XSS/traversal routes). ## IMPORTANT: keep `swap()` in sync with real run flags The current `swap()` recreates containers with only `--name` and `-p`. It does **not** yet reproduce any bind mounts (e.g. the SQLite `demo.db`), config-file mounts, or network/DNS-alias flags the real containers depend on. **Before relying on the script, confirm the actual `podman run` flags** the current `backend-api` and `frontend-vue` were created with and fold them into `swap()`, or a deploy will silently drop them. To inspect what the running containers actually use: ```bash podman --connection gcp-lab inspect backend-api | jq '.[0].Mounts, .[0].NetworkSettings' podman --connection gcp-lab inspect frontend-vue | jq '.[0].Mounts, .[0].NetworkSettings' ``` ## Caddy config / cert deploys The Caddyfile and certs live on the host at `~/imperva-proxy/` and are bind-mounted read-only into `caddy-proxy`. To change routing: 1. Edit `imperva-proxy/Caddyfile` in the repo, copy it to the host `~/imperva-proxy/`. 2. Validate before restarting so you're not debugging from a crash loop: ```bash podman --connection gcp-lab exec caddy-proxy caddy validate --config /etc/caddy/Caddyfile ``` 3. `./deploy.sh --restart-caddy`. If the container is already crash-looping and `exec` won't attach, validate against the mount with a throwaway container instead: ```bash podman run --rm -v /home/matt/imperva-proxy/Caddyfile:/etc/caddy/Caddyfile:ro,Z \ docker.io/library/caddy:2-alpine caddy validate --config /etc/caddy/Caddyfile ``` ## Verifying a deploy - Origin health from the host (self-signed, so `-k`): ```bash podman --connection gcp-lab exec caddy-proxy sh -c 'wget -qO- --no-check-certificate https://localhost/ | head' ``` (or `curl -vk https://localhost/` directly on the host) - Real path end to end: load `https://flytoouter.space/` in a browser — should show Imperva's trusted edge cert while the origin serves self-signed to Imperva only.