docs(deploy): document the update path — up -d alone ships nothing
The README only ever described a fresh install. There was no update section anywhere, and `--build` appeared nowhere in the docs. That matters because `app` and `frontend` are `build:` services with no published image tag, and Compose has no source-change detection: if an image by that name exists it is reused. So the natural `git pull && docker compose up -d` reports "Container app-1 Running", rebuilds nothing, and exits 0. A deploy that shipped none of the new code is indistinguishable from a successful one — which is how eleven merged fixes can sit in the repo and never reach the box. Verified both halves against a real stack rather than asserting them: with a source change staged, `up -d` left the image ID untouched; `up -d --build` produced a new image ID and a healthy /health. Adds an "Updating an existing deployment" section covering backup-before-migrate, pull, rebuild, health check, and an image-ID comparison to prove a build actually happened. Also spells out the rollback trap: migrations run on boot and are not undone by checking out an older commit, so rolling back code without restoring the snapshot leaves the schema ahead of the binary and the app refusing to start. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
39
README.md
39
README.md
@@ -115,6 +115,45 @@ Caddy automatically obtains a Let's Encrypt certificate on first start. The app
|
||||
> docker compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||
> ```
|
||||
|
||||
### Updating an existing deployment
|
||||
|
||||
> **`docker compose up -d` alone will NOT deploy your changes.** `app` and `frontend` are
|
||||
> `build:` services with no published image tag, and Compose has no source-change detection:
|
||||
> if an image with that name already exists it is reused. After a `git pull` the command
|
||||
> reports `Container … Running`, changes nothing, and **exits 0** — so a deploy that shipped
|
||||
> nothing looks exactly like a successful one. `--build` is what makes it real.
|
||||
|
||||
```bash
|
||||
cd /path/to/eventsnap
|
||||
|
||||
# 1. Back up first — migrations run automatically on boot and are not reversible in place.
|
||||
# (See "Backup" below; the database dump is the one that matters here.)
|
||||
|
||||
# 2. Fetch the new code.
|
||||
git pull
|
||||
|
||||
# 3. Rebuild and restart. --build is NOT optional.
|
||||
docker compose up -d --build
|
||||
|
||||
# 4. Confirm the app came back up. Anything other than "ok" means check the logs.
|
||||
curl -fsS https://DOMAIN/health && echo
|
||||
|
||||
# 5. Confirm a NEW image was actually built. Note the IMAGE ID before you start and
|
||||
# compare — it must have changed. (Ignore the CREATED column; it reports the base
|
||||
# layer's age, not this build's.) An unchanged ID means step 3 ran without --build
|
||||
# and you are still serving the old code.
|
||||
docker compose images app frontend
|
||||
```
|
||||
|
||||
Migrations are applied by the backend on startup, so step 3 covers them. If `app` stays
|
||||
unhealthy afterwards, `docker compose logs app` will name the failing migration — and note
|
||||
that a migration applied by a *newer* build is not removed by checking out an older commit,
|
||||
so rolling back code without restoring the database snapshot from step 1 leaves the schema
|
||||
ahead of the binary and the app refusing to boot.
|
||||
|
||||
Only the two application services rebuild; `db` and `caddy` are pinned upstream images and
|
||||
are untouched, so data volumes and the TLS certificate survive.
|
||||
|
||||
### Generate required secrets
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user